package edu.ntu.cloudroid.xacml.finder;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;


import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.sun.xacml.AbstractPolicy;
import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.PolicyReference;
import com.sun.xacml.PolicySet;
import com.sun.xacml.Target;
import com.sun.xacml.combine.PolicyCombiningAlgorithm;
import com.sun.xacml.finder.PolicyFinder;
import com.sun.xacml.finder.PolicyFinderModule;
import com.sun.xacml.finder.PolicyFinderResult;

import edu.ntu.cloudroid.model.IDs;
import edu.ntu.cloudroid.model.friend.GroupData;
import edu.ntu.cloudroid.model.friend.GroupInfo;
import edu.ntu.cloudroid.model.policy.AbstractPrivacyPreference;
import edu.ntu.cloudroid.model.profile.ProfileData;
import edu.ntu.cloudroid.server.backendservice.GroupService;
import edu.ntu.cloudroid.server.backendservice.PolicyService;
import edu.ntu.cloudroid.server.backendservice.ProfileService;
import edu.ntu.cloudroid.xacml.Utilities;
import edu.ntu.cloudroid.xacml.XACMLException;
import edu.ntu.cloudroid.xacml.XACMLStrings;
import edu.ntu.cloudroid.xacml.builder.policy.AbstractPolicyBuilder;
import edu.ntu.cloudroid.xacml.cache.DataService;
import edu.ntu.cloudroid.xacml.combining.ClouDroidPolicyCombiningAlgorithm;

public class ClouDroidPolicyFinderModule extends PolicyFinderModule {
	// the logger we use
	private static final Logger logger = Logger
			.getLogger(ClouDroidPolicyFinderModule.class.getName());

	/**
	 * Basic constructor where you can initialize private module data.
	 */
	public ClouDroidPolicyFinderModule() {		
	}

	/**
	 * Always return true, since indeed this class supports references.
	 * 
	 * @return true
	 */
	@Override
	public boolean isIdReferenceSupported() {
		return true;
	}

	@Override
	public boolean isRequestSupported() {
		return true;
	}

	@Override
	public PolicyFinderResult findPolicy(URI idReference, int type) {		
		if (!idReference.toString().startsWith(XACMLStrings.POLICY_NS)) {
			logger.log(Level.INFO, "Cannot find policy that matches namespace");
			return new PolicyFinderResult();
		}

		if (type != PolicyReference.POLICYSET_REFERENCE) {
			logger.log(Level.INFO, "This finder only matches PolicySet");
			return new PolicyFinderResult();
		}

		try {
			// TODO: check cached results for policy

			PolicySet policySet = getPolicySet(getResourcePersonKey(idReference));

			// TODO: cache new policy

			// now return the policy
			return new PolicyFinderResult(policySet);
		} catch (Exception e) {
			logger.log(Level.SEVERE, "Find by URI: " + e.getMessage());
		} 

		return new PolicyFinderResult();
	}

	@Override
	public PolicyFinderResult findPolicy(EvaluationCtx context) {		
		try {
			// TODO: check cached results for policy			
			PolicySet policySet = getPolicySet(Utilities
					.getResourcePersonKey(context));			
			// TODO: cache new policy

			// now return the policy
			return new PolicyFinderResult(policySet);
		} catch (Exception e) {
			logger.log(Level.SEVERE, "Find by EvaluationCtx: " + e.getMessage());
		} 

		return new PolicyFinderResult();
	}
	
	
	private PolicySet getPolicySet(Key resourcePersonKey)
         throws Exception
   {	   
		try{
      URI id = getPolicyId(resourcePersonKey);

      List<AbstractPolicy> policyList = new ArrayList<AbstractPolicy>();
      Target defaultTarget = null;
            
      ProfileData resource = ProfileService.getProfile(resourcePersonKey);  
      AbstractPrivacyPreference pref = PolicyService.getDefaultPolicy(resource);     
//	  AbstractPrivacyPreference pref = AbstractPrivacyPreferenceDao
//				.getPrivacyPreferenceForPerson(resource);
      
      // Get default policy first
      AbstractPolicyBuilder builder = pref.getPolicyBuilder();
      builder.init(resource, pref);
    		      		 
      AbstractPolicy policy = builder.getPolicy();

      // Copy default target
      defaultTarget = policy.getTarget();
      policyList.add(policy);

      List<GroupData> groups = GroupService.getGroupByOwner(resource.getEmailAddress());
      //DataService.getGroupsForPerson(resource.getKey());

      if (groups!=null)
      // Add all group policies
      for (GroupData g : groups) {
    	  AbstractPrivacyPreference app = PolicyService.getGroupPolicy(g);
    	  if (app==null)
    		  continue;
    	  builder = app.getPolicyBuilder();     
    	  builder.init(resource, g);
    	  policyList.add(builder.getPolicy());
      }
      
      PolicyCombiningAlgorithm alg = IDs
				.getPolicyCombiningAlgorithm(resource
							.getPolicyCombiningAlgId());
	
      return new PolicySet(id, alg, defaultTarget, policyList);
		}
      catch (Exception e){
			e.printStackTrace();
		}
		return null;
   }

	private URI getPolicyId(Key resourcePersonKey) throws URISyntaxException {
		return new URI(XACMLStrings.POLICY_NS + "policy-"
				+ KeyFactory.keyToString(resourcePersonKey));
	}

	private Key getResourcePersonKey(URI policyId) {
		String uriString = policyId.toString();
		return KeyFactory.stringToKey(uriString
				.substring((XACMLStrings.POLICY_NS + "policy-").length()));
	}

	@Override
	public void init(PolicyFinder finder) {
		// Nothing to do here
	}
}
